{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading data set...\n",
      "Image array shape: (385, 38400)\n",
      "Label array shape: (385, 4)\n",
      "Data set load duration: 0.0\n",
      "Training MLP...\n",
      "Training duration: 297.0\n",
      "Train set error: 16.02\n",
      "Test set error: 31.25\n"
     ]
    }
   ],
   "source": [
    "#MLP神经网络训练模块：使用反向传播训练神经网络\n",
    "\n",
    "import cv2\n",
    "import glob\n",
    "import numpy as np\n",
    "from sklearn.model_selection import train_test_split\n",
    "import sys\n",
    "import time\n",
    "\n",
    "\n",
    "def retrieve_data_set():\n",
    "    \"\"\"从所有.npz文件中检索数据并将其聚合到MLP培训数据集\"\"\"\n",
    "    #记录训练数据加载的开始时间\n",
    "    start_time = cv2.getTickCount()\n",
    "    # cv2.getTickCount()函数返回从参考点到这个函数被执行的时钟数\n",
    "\n",
    "    print(\"Loading data set...\")\n",
    "\n",
    "    #载入训练数据\n",
    "    image_array = np.zeros((1, 38400), 'float') #生成一个1行，38400列的零矩阵\n",
    "    label_array = np.zeros((1, 4), 'float') #生成一个1行，4列的零矩阵\n",
    "\n",
    "    # 检索与以下匹配的路径名列表\n",
    "    data_set = glob.glob(\"D:/F/Study/jupyter_notebook/training_data/*.npz\") #搜寻指定文件位置\n",
    "\n",
    "    if not data_set:\n",
    "        print(\"No data set in directory, exiting!\")\n",
    "        sys.exit()\n",
    "\n",
    "    for single_npz in data_set:  #一个一个的读取训练数据\n",
    "        with np.load(single_npz) as data:\n",
    "            temp_images = data[\"train\"]   #训练的图片作为训练的依据\n",
    "            temp_labels = data[\"train_labels\"]   # 训练标签\n",
    "\n",
    "        #对每张图片，空白图像和训练图像上下合并\n",
    "        image_array = np.vstack((image_array, temp_images)) #把获取的训练图像与生成的空白图像合并\n",
    "        label_array = np.vstack((label_array, temp_labels)) #把标签和空白数组合并\n",
    "\n",
    "    X = np.float32(image_array[1:, :])\n",
    "    Y = np.float32(label_array[1:, :])\n",
    "    print(\"Image array shape: {0}\".format(X.shape))\n",
    "    print(\"Label array shape: {0}\".format(Y.shape))\n",
    "\n",
    "    #输出载入图片的时间\n",
    "    end_time = cv2.getTickCount()\n",
    "    print(\"Data set load duration: {0}\"\n",
    "          .format((end_time - start_time) // cv2.getTickFrequency()))\n",
    "    #cv2.getTickFrequency()返回时钟频率。\n",
    "\n",
    "    return X, Y\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    X, Y = retrieve_data_set()\n",
    "\n",
    "    # 8:2 分给训练和测试数据\n",
    "    train_X, test_X, train_Y, test_Y = train_test_split(X, Y, test_size=0.2)\n",
    "\n",
    "    #创建一个多层感知机\n",
    "    start_time = cv2.getTickCount()\n",
    "    #记录训练的开始时间\n",
    "\n",
    "    layer_sizes = np.int32([38400, 64, 4])\n",
    "    #设置层数，输入层38400（像素320*240），输出层4（上下左右四个方向），以及中间层32\n",
    "    model = cv2.ml.ANN_MLP_create()  #建立模型\n",
    "    model.setLayerSizes(layer_sizes)\n",
    "    model.setTrainMethod(cv2.ml.ANN_MLP_BACKPROP) #设置训练方式为反向传播\n",
    "    model.setBackpropMomentumScale(0.0)\n",
    "    #惯性项的强度（前两次迭代的权重之差）；\n",
    "    #该参数提供了一些惯性来平滑权重的随机波动；\n",
    "    model.setBackpropWeightScale(0.001) #权重梯度项的强度\n",
    "    model.setTermCriteria((cv2.TERM_CRITERIA_COUNT | cv2.TERM_CRITERIA_EPS, 500, 0.0001)) #指定停止条件\n",
    "    #设置终止条件，可以指定最大迭代次数（maxCount）或迭代之间的误差变化大小（epsilon）\n",
    "    model.setActivationFunction(cv2.ml.ANN_MLP_SIGMOID_SYM, 2, 1)\n",
    "\n",
    "    print(\"Training MLP...\")\n",
    "    model.train(train_X, cv2.ml.ROW_SAMPLE, train_Y)\n",
    "    #这个函数利用输入的特征向量和对应的响应值(responses)来训练统计模型\n",
    "    #tflag=CV_ROW_SAMPLE表示特征向量以行向量存储\n",
    "\n",
    "    end_time = cv2.getTickCount() #记录训练停止时间，输出训练持续时间\n",
    "    duration = (end_time - start_time) // cv2.getTickFrequency()\n",
    "    #\" // \" 表示整数除法,返回不大于结果的一个最大的整数\n",
    "    print(\"Training duration: {0}\".format(duration))\n",
    "\n",
    "    # 在训练集中的预测准确率,输出错误率\n",
    "    ret_train, resp_train = model.predict(train_X)\n",
    "    train_mean_sq_error = ((resp_train - train_Y) * (resp_train - train_Y)).mean()\n",
    "    print(\"Train set error: {0:.2f}\".format(train_mean_sq_error * 100))\n",
    "\n",
    "\n",
    "    # 在测试数据的预测准确率，输出错误率\n",
    "    ret_test, resp_test = model.predict(test_X)\n",
    "    test_mean_sq_error = ((resp_test - test_Y) * (resp_test - test_Y)).mean()\n",
    "    print(\"Test set error: {0:.2f}\".format(test_mean_sq_error * 100))\n",
    "\n",
    "    # 保存模型\n",
    "    model.save(\"D:/F/Study/jupyter_notebook/mlp_xml/mlp_{0}.xml\".format(str(int(time.time()))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
